#include "gfx-texture.h"
#include <vulkan/vulkan.h>
#define STB_IMAGE_IMPLEMENTATION
#include "../../libs/stb/stb_image.h"
#include "gfx-mgr.h"

GfxTexture::GfxTexture(std::string path)
{
    std::cout << "start create texture...:" << path << std::endl;

    this->_path = path;
    // 加载纹理数据（假设已有纹理数据）
    this->_pixels = stbi_load(this->_path.c_str(), &this->_width, &this->_height, &this->_channels, STBI_rgb_alpha);
    this->_imageSize = this->_width * this->_height * this->_channels;

    this->_createTextureImage();
    this->_createTextureImageView();
    this->_createTextureSampler();
}

GfxTexture::GfxTexture(std::string path, const void *pixels, int width, int height, int channels, uint64_t imageSize)

{
    std::cout << "start create texture...:" << path << std::endl;
    this->_path = path;
    this->_width = width;
    this->_height = height;
    this->_channels = channels;
    this->_imageSize = imageSize;
    this->_pixels = pixels;
    this->_createTextureImage();
    this->_createTextureImageView();
    this->_createTextureSampler();
}
void GfxTexture::_createTextureImage()
{
    std::cout << "this->_width: " << this->_width << std::endl;
    std::cout << "this->_height: " << this->_height << std::endl;
    std::cout << "this->_channels: " << this->_channels << std::endl;

    // 创建暂存缓冲区
    VkBuffer stagingBuffer;
    VkDeviceMemory stagingBufferMemory;

    GfxMgr::getInstance()->createBuffer(
        VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
        &stagingBuffer,
        &stagingBufferMemory,
        this->_imageSize, nullptr);
    // 复制数据到暂存缓冲区
    void *data;
    std::cout << "this->_pixels: " << this->_pixels << std::endl;

    vkMapMemory(GfxMgr::getInstance()->getVulkanDevice(), stagingBufferMemory, 0, this->_imageSize, 0, &data);
    memcpy(data, this->_pixels, static_cast<size_t>(this->_imageSize));
    vkUnmapMemory(GfxMgr::getInstance()->getVulkanDevice(), stagingBufferMemory);

    // 创建纹理图像
    this->_createImage(this->_width, this->_height, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_TILING_OPTIMAL,
                       VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
                       VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
                       this->_textureImage, this->_textureImageMemory);
    // 转换布局并复制数据
    // VK_IMAGE_LAYOUT_UNDEFINED 待确定
    GfxMgr::getInstance()->transitionImageLayout(this->_textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
    GfxMgr::getInstance()->copyBufferToImage(stagingBuffer, this->_textureImage, static_cast<uint32_t>(this->_width), static_cast<uint32_t>(this->_height));
    GfxMgr::getInstance()->transitionImageLayout(this->_textureImage, VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);

    // 清理暂存资源
    vkDestroyBuffer(GfxMgr::getInstance()->getVulkanDevice(), stagingBuffer, nullptr);
    vkFreeMemory(GfxMgr::getInstance()->getVulkanDevice(), stagingBufferMemory, nullptr);
}

void GfxTexture::_createTextureImageView()
{
    VkImageViewCreateInfo viewInfo{};
    viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
    viewInfo.image = this->_textureImage;
    viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
    viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
    viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
    viewInfo.subresourceRange.baseMipLevel = 0;
    viewInfo.subresourceRange.levelCount = 1;
    viewInfo.subresourceRange.baseArrayLayer = 0;
    viewInfo.subresourceRange.layerCount = 1;

    if (vkCreateImageView(GfxMgr::getInstance()->getVulkanDevice(), &viewInfo, nullptr, &this->_textureImageView) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to create texture image view!");
    }
    std::cout << "create texture image view success..." << std::endl;
}
void GfxTexture::_createTextureSampler()
{
    VkSamplerCreateInfo samplerInfo{};
    samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
    samplerInfo.magFilter = VK_FILTER_LINEAR; // VK_FILTER_LINEAR
    samplerInfo.minFilter = VK_FILTER_LINEAR;
    samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
    samplerInfo.anisotropyEnable = VK_FALSE;
    samplerInfo.maxAnisotropy = 1.0f;
    samplerInfo.borderColor = VK_BORDER_COLOR_INT_TRANSPARENT_BLACK;
    samplerInfo.unnormalizedCoordinates = VK_FALSE;
    samplerInfo.compareEnable = VK_FALSE;
    samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
    samplerInfo.minLod = 0.0f;
    samplerInfo.maxLod = 0.0f;
    samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;

    if (vkCreateSampler(GfxMgr::getInstance()->getVulkanDevice(), &samplerInfo, nullptr, &this->_textureSampler) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to create texture sampler!");
    }
    std::cout << "create texture sampler success..." << std::endl;
}

// 以下实现辅助方法...
void GfxTexture::_createImage(uint32_t width, uint32_t height, VkFormat format,
                              VkImageTiling tiling, VkImageUsageFlags usage,
                              VkMemoryPropertyFlags properties,
                              VkImage &image, VkDeviceMemory &imageMemory)
{
    VkImageCreateInfo imageInfo{};
    imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
    imageInfo.imageType = VK_IMAGE_TYPE_2D;
    imageInfo.extent.width = width;
    imageInfo.extent.height = height;
    imageInfo.extent.depth = 1;
    imageInfo.mipLevels = 1;
    imageInfo.arrayLayers = 1;
    imageInfo.format = format;
    imageInfo.tiling = tiling;
    imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    imageInfo.usage = usage;
    imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
    imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;

    if (vkCreateImage(GfxMgr::getInstance()->getVulkanDevice(), &imageInfo, nullptr, &image) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to create image!");
    }

    VkMemoryRequirements memRequirements;
    vkGetImageMemoryRequirements(GfxMgr::getInstance()->getVulkanDevice(), image, &memRequirements);

    VkMemoryAllocateInfo allocInfo{};
    allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    allocInfo.allocationSize = memRequirements.size;
    allocInfo.memoryTypeIndex = GfxMgr::getInstance()->getMemoryTypeIndex(memRequirements.memoryTypeBits, properties);

    if (vkAllocateMemory(GfxMgr::getInstance()->getVulkanDevice(), &allocInfo, nullptr, &imageMemory) != VK_SUCCESS)
    {
        throw std::runtime_error("failed to allocate image memory!");
    }

    vkBindImageMemory(GfxMgr::getInstance()->getVulkanDevice(), image, imageMemory, 0);
    std::cout << "create texture image success..." << std::endl;
}

VkImageView GfxTexture::getImageView()
{
    return this->_textureImageView;
}
VkSampler GfxTexture::getSampler()
{
    return this->_textureSampler;
}

GfxTexture::~GfxTexture()
{
    // 销毁采样器
    if (this->_textureSampler != VK_NULL_HANDLE)
    {
        vkDestroySampler(GfxMgr::getInstance()->getVulkanDevice(), this->_textureSampler, nullptr);
        this->_textureSampler = VK_NULL_HANDLE;
    }

    // 销毁图像视图
    if (this->_textureImageView != VK_NULL_HANDLE)
    {
        vkDestroyImageView(GfxMgr::getInstance()->getVulkanDevice(), this->_textureImageView, nullptr);
        this->_textureImageView = VK_NULL_HANDLE;
    }

    // 销毁图像
    if (this->_textureImage != VK_NULL_HANDLE)
    {
        vkDestroyImage(GfxMgr::getInstance()->getVulkanDevice(), this->_textureImage, nullptr);
        this->_textureImage = VK_NULL_HANDLE;
    }

    // 释放图像内存
    if (this->_textureImageMemory != VK_NULL_HANDLE)
    {
        vkFreeMemory(GfxMgr::getInstance()->getVulkanDevice(), this->_textureImageMemory, nullptr);
        this->_textureImageMemory = VK_NULL_HANDLE;
    }

    // 释放 STB 图像数据（如果是通过 stbi_load 加载的）
    if (this->_pixels && this->_path != "")
    {
        stbi_image_free((void *)this->_pixels);
        this->_pixels = nullptr;
    }

    std::cout << "Texture destroyed: " << this->_path << std::endl;
}
